use mobc::Pool;
use mobc_redis::RedisConnectionManager;
use mobc_redis::{redis, Connection};

// 初始化连接池对象
lazy_static! {
    pub static ref REDISPOOL: Pool<RedisConnectionManager> = {
        println!("lazy_static: 初始化Redis数据库连接池...");
        redis_pool()
    };
}

// 创建连接池,使用默认端口，未使用密码。选择数据库1
// URL格式 redis://[:<passwd>@]<hostname>[:port][/<db>]
// 后期改为Props读取ini
pub fn redis_pool() -> Pool<RedisConnectionManager> {
    let client = match redis::Client::open("redis://127.0.0.1/1") {
        Ok(n) => n,
        Err(err) => {
            panic!("Redis连接失败: {}", err);
        }
    };
    let manager = RedisConnectionManager::new(client);
    let pool = Pool::builder().max_open(10).build(manager);
    pool
}

//Ping redis 为了检验连接可用
pub async fn ping_redis() {
    let pool = REDISPOOL.clone();
    let mut conn = pool.get().await.unwrap();
    let s: String = redis::cmd("PING")
        .query_async(&mut conn as &mut Connection)
        .await
        .unwrap();
    //println!("Redis ping {}", s)
    assert_eq!(s.as_str(), "PONG");
}

// Redis操作结构体
// 全部使用异步完成
pub struct RedisExec<'a> {
    conn: &'a Pool<RedisConnectionManager>,
}

impl<'a> RedisExec<'a> {
    // 生成对象
    pub fn new(pool: &'a Pool<RedisConnectionManager>) -> Self {
        RedisExec { conn: pool }
    }
    // 测试时候可用，主要检测结构体内是否执行tokio
    #[allow(dead_code)]
    pub fn ping(&self) {
        let pool = self.conn.clone();
        tokio::spawn(async move {
            let mut conn = pool.get().await.unwrap();
            let s: String = redis::cmd("PING")
                .query_async(&mut conn as &mut Connection)
                .await
                .unwrap();
            println!("RedisExec  ping {}", s)
        });
    }
    // 添加数据
    #[allow(dead_code)]
    pub fn set(&self, key: String, value: String) {
        let pool = self.conn.clone();
        tokio::spawn(async move {
            let k = key.as_str();
            let v = value.as_str();
            let mut conn = pool.get().await.unwrap();
            let () = redis::cmd("SET")
                .arg(&[k, v])
                .query_async(&mut conn as &mut Connection)
                .await
                .unwrap();
        });
    }
    // 获取数据
    #[allow(dead_code)]
    pub fn get<F>(&self, key: String, func: F)
    where
        F: Fn(String) + Send + Sync + 'static,
    {
        let pool = self.conn.clone();
        //let (mut tx , mut rx) = tokio::sync::mpsc::channel::<String>(1);
        tokio::spawn(async move {
            let k = key.as_str();
            let mut conn = pool.get().await.unwrap();
            let result: redis::RedisResult<String> = redis::cmd("GET")
                .arg(&[k])
                .query_async(&mut conn as &mut Connection)
                .await;
            let r = match result {
                Ok(n) => n,
                Err(err) => {
                    panic!("Redis数据解析失败: {}", err);
                }
            };
            println!("获取的结果是： {}", r);
            // 闭包
            func(r);
            //tx.send(r).await.unwrap();
            // tokio::spawn(async move{
            //     let result = rx.recv().await.unwrap();
            //     println!("获取的结果是： {}",result);
            //  });
        });
    }

    // 获取List数据
    //#[allow(dead_code)]
    pub fn get_list<F>(&self, listname: String, func: F)
    where
        F: Fn(String) + Send + Sync + 'static,
    {
        let pool = self.conn.clone();
        tokio::spawn(async move {
            let k = listname.as_str();
            let mut conn = pool.get().await.unwrap();
            //Result<T, RedisError> ,
            let result: redis::RedisResult<Vec<String>> = redis::cmd("BLPOP")
                .arg(&[k, "1000"])
                .query_async(&mut conn as &mut Connection)
                .await;
            let r = match result {
                Ok(n) => n,
                Err(err) => {
                    panic!("Redis数据解析失败: {}", err);
                }
            };
            //println!("获取的结果是： {}", r[1]);
            // 闭包
            func(r[1].clone());
        });
    }
    // 设置List数据
    //#[allow(dead_code)]
    pub fn list_rpush(&self, listname: String, value: String) {
        let pool = self.conn.clone();
        tokio::spawn(async move {
            let listname = listname.as_str();
            let value = value.as_str();
            let mut conn = pool.get().await.unwrap();
            let () = redis::cmd("RPUSH")
                .arg(&[listname, value])
                .query_async(&mut conn as &mut Connection)
                .await
                .unwrap();
        });
    }
}
